<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>123
    <div id="app"></div>
</body>
<script>
    function h(tag, props, children) {
        return {
            tag, props, children
        }
    }
    function patch(n1, n2) {
        // console.log(n1, n2)
        let children1 = n1.children
        let children2 = n2.children
        let el = n2.el = n1.el
        let parent = n2.parent = n1.parent
        if (n1.tag !== n2.tag) {
            if (parent.contains(el)) {
                parent.removeChild(el)
            }
            mount(n2, parent)
        }
        else {

            if (typeof children2 === "string") {
                // console.log(el, n1, n2, children1, children2)
                if (children1 !== children2) {

                    el.textContent = children2
                }
            }
            else if (typeof children1 === "string") {
                el.innerHTML = ''
                if (children2) {
                    children2.forEach(child => {
                        mount(child, el)
                    })
                }
            }
            else {
                let n1len = n1.children?.length || 0
                let n2len = n2.children?.length || 0
                const minlen = Math.min(n1len, n2len)
                for (let i = 0; i < minlen; i++) {
                    patch(n1.children[i], n2.children[i])
                }
                if (n1len >= n2len) {
                    n1.children.slice(minlen).forEach(child => {
                        el.removeChild(child.el)
                    })
                }
                else {
                    n2.children.slice(minlen).forEach(child => {
                        mount(child, el)
                    })
                }
            }
        }
    }
    function mount(vnode, container) {
        let el = document.createElement(vnode.tag)
        if (vnode.props) {
            for (let i in vnode.props) {
                if (i.startsWith('on')) {
                    el.addEventListener(i.substring(2), vnode.props[i])
                }
                else {
                    el.setAttribute(i, vnode.props[i])
                }
            }
        }
        if (vnode.children) {

            if (typeof vnode.children === 'string') {
                el.textContent = vnode.children
            }
            else {
                vnode.children.forEach(child => {
                    mount(child, el)
                })
            }
        }
        vnode.el = el
        vnode.parent = container
        container.appendChild(el)
    }
    const targetMap = new Map()
    const targetSet = new Set()
    function reactive(obj) {
        if (!targetMap[obj]) {

            targetMap[obj] = new Proxy(obj, {
                get(target, name) {
                    targetSet.add(target)
                    return target[name]
                },
                set(target, name, value) {
                    target[name] = value
                    if (targetSet.has(target)) {
                        notify()
                    }
                }
            })
        }
        return targetMap[obj]
    }
    const App = {
        data: reactive({
            id: 0,
            name: 'ss'
        }),
        render() {
            // console.log(App.data.id)
            let arr = []
            for (let i = 0; i < App.data.id; i++) {
                arr.push(h('div', null, App.data.id + '_' + i))
            }
            arr.push(h('input', {
                'value': App.data.name, 'oninput': (e) => {
                    App.data.name = e.target.value
                }
            }, [])
            )
            arr.push(h('span', null, App.data.name))
            console.log(arr)
            return h('div', null, [...arr, h('button', {
                'onclick': () => {
                    App.data.id++
                },
            }, '加加')])
        }
    }


    let notifyFun
    function notify() {
        notifyFun && notifyFun()
    }
    function watchEffect(fn) {
        notifyFun = fn
        notifyFun()
    }
    function mountApp(compoent, container) {
        let isMounted = false
        let preNode = compoent.render()
        watchEffect(() => {
            // console.log(isMounted)
            if (isMounted) {
                let newNode = compoent.render()
                patch(preNode, newNode)
                preNode = newNode
            }
            else {
                mount(preNode, container)
                isMounted = true
            }
        })

    }
    mountApp(App, document.getElementById('app'))
</script>

</html>